package com.komeri.komeriapl;

import static com.komeri.komeriapl.TestConstants.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.json.JSONException;
import org.json.JSONObject;

import android.os.Build;
import android.util.Log;

/**
 * サードパーティアプリケーションサーバー通信クラス
 */
public class ServerUtilities {
	
	/** Log出力用のタグ */
	private static final String TAG = ServerUtilities.class.getSimpleName();
	
	/** レジストレーションIDを管理し、プッシュ通知を配信するサードパーティアプリケーションサーバーのURL */
	private static final String SERVER_URL = "http://114.179.61.140/Smt/KSmtDvcRegInfo.jsp?";

	/** HTTPボティ部のキー（OS名） */
	private static final String OS_KEY = "os";
	
	/** HTTPボティ部のキー（レジストレーションID） */
	private static final String REG_ID_KEY = "id";
	
	/** HTTPボティ部のキー（端末のOSバージョン） */
	private static final String OS_VERSION_KEY = "ver";
	
	/** HTTPボティ部のキー（端末のモデル名） */
	private static final String OS_MODEL_KEY = "model";
	
	/** HTTPボティ部のキー（アプリバージョン） */
	private static final String APP_VERSION_KEY = "appver";
	
	/**	サーバーに通信を試みる最大回数 */
	private static final int MAX_ATTEMPTS = 5;
	
	/** サーバーへの通信をリトライする際の、待ち時間の基準値 */
	private static final int BACKOFF_MILLI_SECONDS = 2000;
	
	/** 乱数発生クラス */
	private static final Random random = new Random();
	
	/** レスポンスのJSONのキー（ステータス） */
	private static final String JSON_STATUS_KEY = "resStat";
	
	/** レスポンスのJSONのキー（アプリ最新バージョン） */
	private static final String JSON_APP_NEW_VERSION_KEY = "appVer";
	
	/**
	 * サードパーティアプリケーションサーバーにレジストレーションIDを登録する
	 * @param context コンテキスト
	 * @param regId　レジストレーションID
	 * @param appNewVer サーバーから取得したアプリの最新バージョン（out）
	 * @return 登録成功:true、失敗:false
	 */
	static boolean register(final String regId, StringBuilder appNewVer) {
		boolean bRet = false;
		
		Map<String, String> params = new HashMap<String, String>();
		params.put(OS_KEY, "02");	// "01"だったらiOS、"02"だったらAndroid
		params.put(REG_ID_KEY, regId);
		params.put(OS_VERSION_KEY, "Android OS " + Build.VERSION.RELEASE);
		params.put(OS_MODEL_KEY, Build.PRODUCT);
		params.put(APP_VERSION_KEY, CommonUtility.appVersion());
		
		// サーバーへの通信をリトライする際の待ち時間を2000～3000ミリ秒の間でランダムに決定する
		long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
		
		for (int i = 1; i <= MAX_ATTEMPTS; i++) {
			Log.d(TAG, "Attempt #" + i + " to register");
			try {
				// サーバーにHTTPをポストする
				boolean status = post(SERVER_URL, params, appNewVer);
				if (status == true) {
					bRet = true;
					break;
				} else {
					// サーバーへのレジストレーションID登録に失敗した場合は例外とし、送信をリトライする
					throw new IOException("Post failed");
				}
			} catch (Exception e) {
				Log.e(TAG, "Failed to register on attempt " + i + ":" + e);
				if (i == MAX_ATTEMPTS) {
					// 通信失敗
					break;
				}
				try {
					// リトライまで待機する
					Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
					Thread.sleep(backoff);
				} catch (InterruptedException e1) {
					// 待機中にアクティビティを終了させるなど、スレッドに割り込みが入った場合はリトライしない
					Log.d(TAG, "Thread interrupted: abort remaining retries!");
					Thread.currentThread().interrupt();
					break;
				}
				
				// リトライ時の待ち時間を2倍する
				backoff *= 2;
			}
		}
		
		return bRet;
	}
	
	/**
	 * POSTコマンドのリクエスト
	 * @param endpoint　サーバーのURL
	 * @param params HTTPのボディに設定する内容
	 * @param appNewVer サーバーから取得したアプリの最新バージョン（out）
	 * @return true:レジストレーションID登録成功、false:登録失敗
	 * @throws IOException
	 */
	private static boolean post(String endpoint, Map<String, String> params, StringBuilder appNewVer) throws IOException {
		boolean bRet = false;
		URL url;
		try {
			url = new URL(endpoint);
		} catch (MalformedURLException e) {
			// 無効な書式のURLが指定された
			throw new IllegalArgumentException("invalid url: " + endpoint);
		}
		
		//　paramsからボディ部を作成
		StringBuilder bodyBuilder = new StringBuilder();
		bodyBuilder.append(REQUEST_TEST_FLG); // trunkの場合は空文字、branchesの場合はパラメータ付与
		
		Iterator<Entry<String, String>> iterator = params.entrySet().iterator();	
		while (iterator.hasNext()) {
			Entry<String, String> param = iterator.next();
			bodyBuilder.append(param.getKey()).append('=').append(param.getValue());
			if (iterator.hasNext()) {
				bodyBuilder.append('&');
			}
		}
		
		String body = bodyBuilder.toString();
		Log.i(TAG, "Posting '" + body + "' to " + url);
		
		// 文字列をバイト配列に変換
		byte[] bytes = body.getBytes();
		
		HttpURLConnection conn = null;
		BufferedReader bis = null;
		
		try {
			// 接続用HttpURLConnectionオブジェクト作成
			conn = (HttpURLConnection) url.openConnection();
			
			// POSTによるデータ送信を可能にする
			conn.setDoOutput(true);
			
			// キャッシュを利用しない
			conn.setUseCaches(false);
			
			// コンテンツ長を設定する
			conn.setFixedLengthStreamingMode(bytes.length);
			
			// リクエストメソッドの設定
			conn.setRequestMethod("POST");
			
			// ヘッダーの設定
			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
			
			// データ取得時のタイムアウト時間（ミリ秒）を設定
			conn.setReadTimeout(3000);
			
			// HTTPリクエストをポストする
			OutputStream out = conn.getOutputStream();
			out.write(bytes);
			out.close();
			Log.d(TAG, "responseCode = " + conn.getResponseCode());
			
			try {
				// JSONデータの文字列を取得する
		    	bis = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
		    	String jsonStr = "";
		    	String data = "";
		    	while ((data = bis.readLine()) != null) {
		    		jsonStr += data;
		    	}
		    	
		    	// JSONオブジェクトに変換する
				JSONObject rootObject = new JSONObject(jsonStr);
				
				// ステータスコード取得
				String status = rootObject.getString(JSON_STATUS_KEY);
				
				if (status.equals("00")) {
					// サーバーでレジストレーションID登録に成功した場合
					Log.d(TAG, "register id on Server success");
					bRet = true;
				} else {
					Log.d(TAG, "register id on Server fail");					
				}
				
				// アプリの最新バージョン取得
				appNewVer.append(rootObject.getString(JSON_APP_NEW_VERSION_KEY));
				
			} catch (JSONException e) {
				e.printStackTrace();
			}
		} finally {
			if (bis != null) {
				bis.close();
			}
			if (conn != null) {
				conn.disconnect();
			}
		}
		
		return bRet;
	}
}

